#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>

using namespace std;

const int defaultnum = 5;

template<class T>
class BlockQueue
{
public:
    //static const int defaultnum = 5;
    BlockQueue(int num = defaultnum)
        :maxcap_(num)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&c_cond_,nullptr);
        pthread_cond_init(&p_cond_,nullptr);
        low_water = maxcap_ / 3;
        high_water = (maxcap_*2)/3;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_,nullptr);
        pthread_cond_destroy(&c_cond_,nullptr);
        pthread_cond_destroy(&p_cond_,nullptr);
    }

    T pop() 
    {
        phtread_mutex_lock(&mutex_);
        while(q_.size() == 0)
        {
            pthread_cond_wait(&c_cond_,&mutex_);
        }

        T out = q_.front();
        q_.pop();

        //如果此时阻塞队列中的数据数量比low_water少,就唤醒生产者线程生产
        if(q_.size() < low_water_)
        {
            //唤醒生产者线程生产数据
            pthread_cond_signal(&c_cond_);
        }
        phtread_mutex_unlock(&mutex_);
        return out;
    }

    void push(const T& in)
    {
        //不把if判断放在加锁上面是因为判断也是计算，也需要上CPU也需要访问临界资源所以要把判断放在加锁下面！！！！
        phtread_mutex_lock(&mutex_);//可能在push的时候有线程在pop,所以为了保证互斥，加锁
        while(q_.size() == maxcap_)
        {
            //如果此时阻塞队列的数据数量等于最大量的话就不能生产了，让生产线程去等待队列等消费线程先消费点数据
            pthread_cond_wait(&p_cond_,&mutex_); //线程加入到生产者线程的等待队列并释放锁资源 等线程被唤醒的时候会重新持有锁
        }

        //走到这里两种情况:1)阻塞队列没有满 2)被唤醒了
        q_.push(in);
        
        //如果此时阻塞队列的数据数量比high_water大了,就唤醒消费者线程消费
        if(q_.size() > high_water_)
        {
            //有数据了就可以唤醒消费者线程了
            pthread_cond_signal(&c_cond_);
        }
        phtread_mutex_unlock(&mutex_);
    }

private:
    queue<T> q_; //因为队列不知道具体放什么数据所以用模板
    int maxcap_; //队列最大能存的最大数据量
    pthread_mutex_t mutex_; //STL容器在设计之初就没有考虑多线程的安全问题,所以我们在用queue容器模拟阻塞队列时就要自己用锁保证queue的安全
    
    /*维护生产者消费者模型的同步问题,不能一直让生产者线程生产,也不能一直让消费者线程消费，所以引入一个条件变量。但是生产者线程和消费者线程不能用一个等待队列所以定义两个
    因为唤醒的时候不能分辨出唤醒的时生产者还是消费者*/
    pthread_cond_t c_cond_; 
    pthread_cond_t p_cond_;

    int low_water;
    int high_water;
};
